import PolyfillPromise from 'promise-polyfill'
import * as esriLoader from 'esri-loader'
import echarts from 'echarts'
import tinyColor from 'tinycolor2'
import TerraformerArcGIS from "terraformer-arcgis-parser";

import administrativeDivision_0_geojson from "../../static/json/ZheJiangDivisions_shi.json"
import administrativeDivision_1_geojson from '../../static/json/ZheJiangDivisions_xian.json'
class MapManager {
  constructor() {
    this.mapViews = []
    esriLoader.utils.Promise = PolyfillPromise
    esriLoader.loadCss(window.g.MapConfiguration.ArcGIS_API_URL + '/esri/css/main.css')
    esriLoader.loadModules(['esri/core/urlUtils', 'esri/config', 'esri/core/watchUtils', 'esri/layers/TileLayer',
      'esri/layers/MapImageLayer', 'esri/layers/FeatureLayer', "esri/layers/GraphicsLayer",
      'esri/geometry/SpatialReference', 'esri/tasks/GeometryService', 'esri/tasks/support/ProjectParameters',
      'esri/tasks/support/Query', 'esri/tasks/QueryTask', 'esri/Graphic',
      'esri/geometry/Point', 'esri/geometry/Polyline', 'esri/geometry/Polygon',
      "esri/renderers/HeatmapRenderer", "esri/renderers/SimpleRenderer",
      "esri/renderers/UniqueValueRenderer", "esri/renderers/ClassBreaksRenderer",
      "esri/geometry/support/jsonUtils"],
      { url: window.g.MapConfiguration.ArcGIS_API_URL + '/init.js' })
      .then(([urlUtils, esriConfig, watchUtils, TileLayer, MapImageLayer, FeatureLayer, GraphicsLayer, SpatialReference, GeometryService,
        ProjectParameters, Query, QueryTask, Graphic, Point, Polyline, Polygon,
        HeatmapRenderer, SimpleRenderer, UniqueValueRenderer, ClassBreaksRenderer, geometryJsonUtils]) => {
        esriConfig.request.timeout = 120000
        if (window.g.MapConfiguration.ProxyFileUrl && window.g.MapConfiguration.ProxyUrls) {
          window.g.MapConfiguration.ProxyUrls.forEach(pu => {
            urlUtils.addProxyRule({
              urlPrefix: pu,
              proxyUrl: window.g.MapConfiguration.ProxyFileUrl
            });
          })
        }
        if (window.g.ArcGIS_Fonts_Path) {
          esriConfig.fontsUrl = window.g.ArcGIS_Static_Path + 'fonts'
        }
        this.TileLayer = TileLayer
        this.MapImageLayer = MapImageLayer
        this.FeatureLayer = FeatureLayer
        this.GraphicsLayer = GraphicsLayer
        this.SpatialReference = SpatialReference
        this.GeometryServiceInstance = new GeometryService({
          url: window.g.MapConfiguration.GeometryServiceURL
        })
        this.ProjectParameters = ProjectParameters
        this.Query = Query
        this.QueryTask = QueryTask
        this.watchUtils = watchUtils
        this.Graphic = Graphic
        this.Point = Point
        this.Polyline = Polyline
        this.Polygon = Polygon
        this.HeatmapRenderer = HeatmapRenderer
        this.SimpleRenderer = SimpleRenderer
        this.UniqueValueRenderer = UniqueValueRenderer
        this.ClassBreaksRenderer = ClassBreaksRenderer
        this.renderer_legends = []

        this.administrativeDivision_0 = []
        this.administrativeDivisions_1 = []

        administrativeDivision_0_geojson.features.forEach((f) => {
          let division = Object.assign({}, f);
          let attributes = {
            name: division.properties.NAME,
            code: division.properties.CODE.substring(0, 6),
          };
          const convertGeoJson = TerraformerArcGIS.convert(division, { sr: 4490 });
          const convertGeo = geometryJsonUtils.fromJSON(convertGeoJson.geometry);
          const labelPosition =
            f.properties.LABELX && f.properties.LABELY
              ? new Point({
                x: Number(f.properties.LABELX),
                y: Number(f.properties.LABELY),
                spatialReference: { wkid: 4490 },
              })
              : convertGeo.centroid;
          this.administrativeDivision_0.push({
            geometry: convertGeo,
            attributes: attributes,
            labelPosition: labelPosition,
          });
        });

        administrativeDivision_1_geojson.features.forEach((f) => {
          let division = Object.assign({}, f);
          let attributes = {
            // name: division.properties.NAME,
            // code: division.properties.CODE.substring(0, 6),
            name: division.properties.name,
            code: division.properties.code,
          };
          const convertGeoJson = TerraformerArcGIS.convert(division, { sr: 4490 });
          const convertGeo = geometryJsonUtils.fromJSON(convertGeoJson.geometry);
          const labelPosition =
            f.properties.labelx && f.properties.labely
              ? new Point({
                x: Number(f.properties.labelx),
                y: Number(f.properties.labely),
                spatialReference: { wkid: 4490 },
              })
              : convertGeo.centroid;
          this.administrativeDivisions_1.push({
            geometry: convertGeo,
            attributes: attributes,
            labelPosition: labelPosition,
          });
        });
        this.timer = null
      })
  }

  /** 存放地图
   * @param mapView
   * @param id
   * @returns {Promise} 操作成功返回true
   */
  saveMapView(mapView, id) {
    return new PolyfillPromise((resolve, reject) => {
      let has = false
      this.mapViews.forEach(mV => {
        if (mV.mapViewID === id && !has) {
          mV.mapView = mapView
          has = true
        }
      })
      if (!has) {
        this.mapViews.push({
          mapView: mapView,
          mapViewID: id
        })
      }
      resolve(true)
    })
  }

  /** 根据移除地图
   * @param mapView
   * @param id
   * @returns {Promise}
   */
  removeMapViewByID(id) {
    return new PolyfillPromise((resolve, reject) => {
      let found = false
      for (let i = 0; i < this.mapViews.length; i++) {
        if (id === this.mapViews[i].mapViewID) {
          this.mapViews[i].mapView.destroy()
          this.mapViews[i].mapView = undefined
          this.mapViews.splice(i, 1)
          found = true
          break
        }
      }
      if (found) {
        resolve(true)
      } else {
        console.warn('未发现要移除的id为：' + id + '的地图视图')
        // reject(false)
      }
    })
  }

  /** 根据id获取视图，异步
   * @param id 地图id
   * @returns {Promise} 返回异步
   */
  getMapViewByIDAsy(id) {
    const self = this
    return new PolyfillPromise((resolve, reject) => {
      let costTime = 0
      const findTask = setInterval(() => {
        costTime++
        if (costTime < 20) {
          for (let i = 0; i < self.mapViews.length; i++) {
            if (id === self.mapViews[i].mapViewID) {
              clearInterval(findTask)
              resolve(self.mapViews[i].mapView)
            }
          }
        } else {
          clearInterval(findTask)
          console.warn('未发现要获取的id为 ' + id + ' 的地图视图')
        }
      }, 500)
    })
  }
  /** 根据id获取视图
   * @param id 地图id
   * @returns  返回MapView对象
   */
  getMapViewByID(id) {
    let has = false
    for (let i = 0; i < this.mapViews.length; i++) {
      if (id === this.mapViews[i].mapViewID) {
        has = true
        return this.mapViews[i].mapView
      }
    }
    if (!has) {
      console.warn('未发现id为 ' + id + ' 的视图')
      return null
    }
  }
  addLayer(options, type, mapView) {
    let Layer = this.MapImageLayer
    if (type === 'tile') {
      Layer = this.TileLayer
    }
    else if (type === 'feature') {
      Layer = this.FeatureLayer
    } else if (type === 'GraphicsLayer') {
      Layer = this.GraphicsLayer
    }
    const layer = new Layer(options)
    mapView.map.add(layer)
    return layer
  }
  /**
   * 根据图形id移除
   * @param {*} mapView
   * @param {*} id  图层名称
   */
  removeLayer(mapView, id) {
    if (!mapView) { return; }
    let layers = mapView.map.layers.items;
    for (let index = 0; index < layers.length; index++) {
      const element = layers[index];
      if (id == element.id) {
        mapView.map.layers.removeAt(index);
        break;
      }
    }
  }

  /**
   * 图层定位通用方法，适应多种情况
   * @param layer
   * @param mapView
   * @param options goTo()参数
   * @returns {Promise}
   */
  goToLayer(layer, mapView, options = { easing: 'ease-out', duration: 1000 }) {
    const self = this
    return new PolyfillPromise((resolve, reject) => {
      if (layer && mapView) {
        if (layer.type === 'graphics') {
          mapView.goTo(layer.graphics, options)
            .then(gs => {
              resolve({ message: '图层定位成功', data: gs })
            })
            .catch(ge => {
              reject({ message: '图层定位失败', data: ge })
            })
        }
        // else if(layer.type === 'tile' || layer.type === 'map-image' || layer.type === 'feature') {
        else if (layer.fullExtent && layer.fullExtent.spatialReference.equals(mapView.spatialReference)) {
          mapView.goTo(layer.fullExtent, options)
            .then(gs => {
              resolve({ message: '图层定位成功', data: gs })
            })
            .catch(ge => {
              reject({ message: '图层定位失败', data: ge })
            })
        }
        else if (layer.fullExtent && (layer.fullExtent.spatialReference.wkid || layer.fullExtent.spatialReference.wkt)) {
          self.projectGeometries([layer.fullExtent], { wkid: mapView.spatialReference.wkid, wkt: mapView.spatialReference.wkt })
            .then(outGs => {
              mapView.goTo(outGs, options)
                .then(gs => {
                  resolve({ message: '图层定位成功', data: gs })
                })
                .catch(ge => {
                  reject({ message: '图层定位失败', data: ge })
                })
            })
            .catch(pe => {
              reject({ message: '参考系转换失败，无法定位', data: pe })
            })
        }
        else if (layer.type === 'feature') {
          layer.queryFeatures()
            .then(response => {
              if (response && response.features && response.features.length > 0) {
                const gs = []
                response.features.forEach(f => {
                  gs.push(f.geometry)
                })
                if (gs[0].spatialReference.equals(mapView.spatialReference)) {
                  mapView.goTo(gs, options)
                    .then(gs => {
                      resolve({ message: '图层定位成功', data: gs })
                    })
                    .catch(ge => {
                      reject({ message: '图层定位失败', data: ge })
                    })
                }
                else {
                  self.projectGeometries(gs, { wkid: mapView.spatialReference.wkid, wkt: mapView.spatialReference.wkt })
                    .then(outGs => {
                      mapView.goTo(outGs, options)
                        .then(gs => {
                          resolve({ message: '图层定位成功', data: gs })
                        })
                        .catch(ge => {
                          reject({ message: '图层定位失败', data: ge })
                        })
                    })
                    .catch(pe => {
                      reject({ message: '图层要素参考系转换失败，无法定位', data: pe })
                    })
                }
              } else {
                reject({ message: '图层要素为空', data: response })
              }
            })
            .catch(qe => {
              reject({ message: '图层要素查询出错，无法定位', data: qe })
            })
        }
        else if (layer.url) {
          const query = new self.Query()
          const queryTask = new self.QueryTask({ url: layer.url })
          query.returnGeometry = true
          query.outFields = ['*']
          query.where = '1=1'
          queryTask.execute(query)
            .then(response => {
              if (response && response.features && response.features.length > 0) {
                const gs = []
                response.features.forEach(f => {
                  gs.push(f.geometry)
                })
                self.projectGeometries(gs, { wkid: mapView.spatialReference.wkid, wkt: mapView.spatialReference.wkt })
                  .then(outGs => {
                    mapView.goTo(outGs, options)
                      .then(gs => {
                        resolve({ message: '图层定位成功', data: gs })
                      })
                      .catch(ge => {
                        reject({ message: '图层定位失败', data: ge })
                      })
                  })
                  .catch(pe => {
                    reject({ message: '图层要素参考系转换失败，无法定位', data: pe })
                  })
              } else {
                reject({ message: '图层要素为空', data: response })
              }
            })
            .catch(qe => {
              reject({ message: '图层要素查询出错，无法定位', data: qe })
            })
        }
        else {
          reject({ message: '定位图层对象有误', data: layer })
        }
        // }
      }
      else {
        reject({ message: '定位对象有误', data: { layer: layer, mapView: mapView } })
      }
    })
  }  
  /**
   * 通用放置地图图表方法
   * @param chartType : 'pie','bar'
   * @param data : [
   * {
   *   name: 名称,
   *   data: [],
   *   seriesColor: []颜色,
   *   geometry: 任意类别空间要素
   * }
   * ]
   * @param mapView 地图视图 :
   * @param chartOption : {
   *   tooltipFormatter : 图表标签格式化 '{a} <br/>{b} : {c}  ({d}%)'
   * }
   */

  setMapCharts(chartType, data, mapView, chartOption) {
    const self = this
    self.renderer_mapView = mapView
    const mapViewElemID = mapView.container.id ? '#' + mapView.container.id : '#main_map'
    self.renderer_chartsData = data
    if (chartType === 'pie') {
      let tooltipFormatterStr = (chartOption && chartOption.tooltipFormatter) ? chartOption.tooltipFormatter : "{a} <br/>{b} : {c}  ({d}%)"
      self.renderer_chartsData.forEach((p, pIndex) => {
        p.id = p.id ? p.id : pIndex
        const option = {
          title: {
            text: p.name,
            // subtext: '',
            left: 'center',
            textStyle: {
              fontSize: 17,
              color: '#fff'
            }
          },
          backgroundColor: 'rgba(255, 255, 255,0)',
          tooltip: {
            trigger: 'item',
            formatter: tooltipFormatterStr
          },
          series: [
            {
              name: p.name,
              type: 'pie',
              radius: '50%',
              center: ['50%', '50%'],
              data: p.data,
              // roseType: 'radius',
              label: {
                normal: {
                  color: '#fff',
                  formatter: '{d}%',
                  position: 'inside'
                }
              },
              itemStyle: {
                normal: {
                  color: function (params) {
                    const num = p.seriesColors.length;
                    return p.seriesColors[params.dataIndex % num]
                  },
                  shadowBlur: 10,
                  shadowColor: 'rgba(0, 0, 0, 0.3)'
                }
              },
              animationType: 'scale',
              animationEasing: 'elasticOut',
              animationDelay: function (idx) {
                return Math.random() * 200;
              }
            }
          ]
        }
        $(mapViewElemID).append("<div style='position: absolute;z-index: 0;width: 120px;height: 120px;background-color: transparent' " +
          "id='map_chart_" + p.id + "'></div>")
        const pElem = document.getElementById("map_chart_" + p.id)
        const pie = echarts.init(pElem);
        pie.setOption(option);
        pie.on('click', function (val) {
          if (val.seriesName === "临安区") {
            const point = new self.Point({
              x: 119.39213605625896,
              y: 30.2038226634905,
              spatialReference: self.renderer_mapView.spatialReference
            })
            self.renderer_mapView.goTo({ target: point, zoom: 15 }, { easing: 'ease-out', duration: 1000 })
          }
          else {
            let geo = p.geometry
            if (p.geometry.type === 'polyline' || p.geometry.type === 'polygon') {
              geo = p.geometry.centroid
            }
            self.renderer_mapView.goTo({ target: geo, zoom: 9 }, { easing: 'ease-out', duration: 1000 })
          }
        })
      })
    }
    else if (chartType === 'bar') {
      let tooltipFormatterStr = (chartOption && chartOption.tooltipFormatter) ? chartOption.tooltipFormatter : "{a} <br/>{b} : {c}  )"
      let yAxisName = (chartOption && chartOption.yAxisName) ? chartOption.yAxisName : '单位：个'
      let allValues = []
      self.renderer_chartsData.forEach((b, bIndex) => {
        b.yAxisData.forEach(v => {
          allValues.push(v)
        })
      })
      self.renderer_chartsData.forEach((b, bIndex) => {
        b.id = b.id ? b.id : bIndex
        const option = {
          title: {
            text: b.name,
            // subtext: '',
            top: 8,
            left: 'center',
            textStyle: {
              fontSize: 16,
              color: 'white',
              textBorderColor: 'black',
              textBorderWidth: 2
            }
          },
          grid: {
            top: 10,
            bottom: 10,
            left: 0
          },
          tooltip: {
            trigger: 'axis',
            axisPointer: {
              type: 'item',
              crossStyle: {
                color: '#999'
              }
            }
          },
          backgroundColor: 'rgba(255, 255, 255,0)',
          xAxis: [
            {
              type: 'category',
              data: b.xAxisData,
              axisPointer: {
                type: 'shadow'
              },
              show: false
            }
          ],
          yAxis: [
            {
              type: 'value',
              name: yAxisName,
              min: 0,
              max: Math.ceil(Math.max(...allValues)),
              interval: Math.ceil((Math.max(...allValues)) / 5),
              axisLabel: {
                formatter: '{value}'
              },
              show: false
            },
          ],
          series: [
            {
              type: 'bar',
              data: b.yAxisData,
              barCategoryGap: 2,
              barWidth: 16,
              itemStyle: {
                color: function (params) {
                  const num = b.xAxisColors.length;
                  const c = new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                    { offset: 0, color: tinyColor(b.xAxisColors[params.dataIndex % num]).brighten(30).toString() },
                    { offset: 0.6, color: b.xAxisColors[params.dataIndex % num] },
                    { offset: 1, color: tinyColor(b.xAxisColors[params.dataIndex % num]).darken(10).toString() }
                  ])
                  return c
                },
                shadowBlur: 4,
                shadowColor: 'rgba(0, 0, 0, 0.3)'
              },
              label: {
                show: true,
                position: 'inside',
                textStyle: {
                  fontSize: 16,
                  color: 'white',
                  fontWeight: 'normal',
                  textBorderColor: 'black',
                  textBorderWidth: 1,
                }
              },
            },
          ]
        }
        const w = (b.xAxisData.length > 5) ? 150 : (20 * b.xAxisData.length)
        $(mapViewElemID).append("<div style='position: absolute;z-index: 0;width: 100px;height: 120px;background-color: transparent' " +
          "id='map_chart_" + b.id + "'></div>")
        $('map_chart_' + b.id).css('width', w + 'px')
        const bElem = document.getElementById("map_chart_" + b.id)
        const bar = echarts.init(bElem);
        bar.setOption(option);
      })
    }
    if (self.extentWatcher) {
      self.extentWatcher.remove()
      self.extentWatcher = undefined
    }
    self.extentWatcher = self.watchUtils.watch(mapView, 'extent', e => {
      self._resetChartsPosition()
    })
    self._resetChartsPosition()
  }
  _resetChartsPosition() {
    if (this.renderer_mapView && this.renderer_chartsData && this.renderer_chartsData.length > 0) {
      this.renderer_chartsData.forEach((p, pIndex) => {
        debugger
        const pie = document.getElementById("map_chart_" + p.id)
        if (pie && p.geometry) {
          let geo = p.geometry
          if (p.geometry.type === 'polyline' || p.geometry.type === 'polygon') {
            geo = p.geometry.centroid
          }
          const sp = this.renderer_mapView.toScreen(geo)
          pie.style.left = sp.x - 60 + 'px'
          pie.style.top = sp.y - 60 + 'px'
          if ((p.chartsVisible || p.chartsVisible === undefined) && p.hasOwnProperty("minScale") && p.hasOwnProperty("maxScale")) {
            if ((p.minScale === 0 || p.minScale >= this.renderer_mapView.scale) && (p.maxScale === 0 || p.maxScale <= this.renderer_mapView.scale)) {
              pie.style.display = "block"
            }
            else {
              pie.style.display = "none"
            }
          }
        }
      })
    }
  }
  setChartsVisible(bool) {
    if (this.renderer_mapView && this.renderer_chartsData && this.renderer_chartsData.length > 0) {
      this.renderer_chartsData.forEach((p, pIndex) => {
        const pie = document.getElementById("map_chart_" + p.id)
        p.chartsVisible = bool
        if (pie && p.geometry) {
          pie.style.display = p.chartsVisible ? "block" : "none"
          if ((p.chartsVisible || p.chartsVisible === undefined) && p.hasOwnProperty("minScale") && p.hasOwnProperty("maxScale")) {
            if ((p.minScale === 0 || p.minScale >= this.renderer_mapView.scale)
              && (p.maxScale === 0 || p.maxScale <= this.renderer_mapView.scale)) {
              pie.style.display = "block"
            }
            else {
              pie.style.display = "none"
            }
          }
        }
      })
    }
  }

  /**
   * 清除地图渲染
   */
  clearMapRenderer() {
    if (this.renderer_mapView) {
      this.renderer_mapView.graphics.removeAll()
    }
    if (this.temporaryLayer && this.renderer_mapView) {
      this.renderer_mapView.map.remove(this.temporaryLayer)
      this.temporaryLayer = undefined
    }
    if (this.renderer_chartsData) {
      this.renderer_chartsData.forEach(p => {
        const pie = document.getElementById('map_chart_' + p.id)
        if (pie) {
          pie.parentNode.removeChild(pie)
        }
      })
      this.renderer_chartsData = undefined
    }
    if (this.renderer_legends.length > 0) {
      this.renderer_legends.forEach(legend => {
        const legendEle = document.getElementById('map_legend_' + legend.id)
        if (legendEle) {
          legendEle.parentNode.removeChild(legendEle)
        }
      })
    }
    if (this.extentWatcher) {
      this.extentWatcher.remove()
      this.extentWatcher = undefined
    }
    this.renderer_mapView = undefined
  }

  getAdministrativeDivisions(type) {
    if (type === 'all') {
      return this.administrativeDivision_0.concat(this.administrativeDivisions_1)
    } else if (type === 1) {
      return this.administrativeDivisions_1
    } else {
      return this.administrativeDivision_0
    }
  }
  /**
   * 获取行政区划数据（异步）
   */
  getAdministrativeDivisionsAsy(type) {
    const self = this
    return new PolyfillPromise((resolve, reject) => {
      const findTask = setInterval(() => {
        let resolveData = []
        if (self.administrativeDivision_0 && self.administrativeDivisions_1) {
          if (type === 'all') {
            resolveData = self.administrativeDivision_0.concat(self.administrativeDivisions_1)
          } else if (type === 1) {
            resolveData = self.administrativeDivisions_1
          } else {
            resolveData = self.administrativeDivision_0
          }
          clearInterval(findTask)
          resolve(resolveData)
        }
      }, 500)
    })
  }
  /**
   * 得到图形center并居中地图
   */
  getGeoCenterGoTo(_mapView, geo) {
    let p = null;
    if (geo.type == "point") {
      p = geo;
    } else if (geo.type == "polyline") {
      p = geo.getPoint(
        0,
        Math.ceil(geo.paths.length / 2)
      );
    } else if (geo.type == "polygon") {
      p = geo.centroid;
    }
    if (p != null)
      _mapView.center = p;
  }
  /**
     * 根据id获取对应的图层
     * @param mapView 地图视图
     * @param divisionUrl 行政区划url
     * @param divisionWhere 行政区划where
     * @param url 
     * @param where
     */
  getLayerFromID(mapView, id) {
    let layers = mapView.map.layers.items;
    for (let index = 0; index < layers.length; index++) {
      const element = layers[index];
      if (id == element.id) {
        return mapView.map.layers.items[index];
      }
    }
  }
}

export default new MapManager()
